﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using SimpleIR;

namespace InterpreterCore
{
	internal partial class LocalCallFrame
	{
		internal void InitializeMain(string[] args, HashSet<IRVarName> lvns, Dictionary<IRVarName, IRType> ttl)
		{
			this.ClearVarsNotInScopeAddNew(lvns, ttl);

			IRSignedIntegerType i32 = GlobalInterpreterState.TProg.TypeTable.Int32Type;
			IRClassType strtype = (IRClassType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForName("System.String");
			IRPtrType strtypeptr = GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor(strtype);
			IRArrayType aofstrtype = (IRArrayType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForName("System.String[]");
			IRPtrType aofstrtypeptr = GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor(aofstrtype);

			CArray stra = MemoryState.AllocateArrayObject(aofstrtype, NumericValue.MakeSignedValueOfType(args.Length, i32));
			for(int i = 0; i < args.Length; ++i)
			{
				this.m_evalstack.PushPointerEvalEntry(new PtrValue(stra.Address, aofstrtypeptr));
				this.PushStringPtrFromCSString_Helper(args[i]);
				this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(i, i32));

				this.SimulateArrayStore(strtypeptr);
			}

			this.m_evalstack.PushPointerEvalEntry(new PtrValue(stra.Address, aofstrtypeptr));
			this.SimulateStoreToLocal(GlobalInterpreterState.TProg.NameTable.LookupLocalVarName("args"));
		}

		internal void SimulateBuiltin(string op)
		{
			if(op.Equals("objcons"))
				this.m_evalstack.DiscardPop();
			else if(op.Equals("array_resize"))
				this.Simulate_ArrayResize();
			else if(op.Equals("array_clone"))
				this.Simulate_ArrayClone();
			else if(op.Equals("valuetype_equals_static"))
				this.Simulate_ValuetypeEqualsStatic();
			else if(op.Equals("valuetype_hashcode_static"))
				this.Simulate_ValuetypeHashcodeStatic();
			else if(op.Equals("enum_compareto"))
				this.Simulate_EnumCompare();
			else if(op.Equals("valuetype_equals"))
				this.Simulate_ValuetypeEquals();
			else if(op.Equals("valuetype_hashcode"))
				this.Simulate_ValuetypeHashcode();
			else if(op.Equals("datetime_to_local"))
				this.Simulate_DateTimeToLocal();
			else if(op.Equals("datetime_to_universal"))
				this.Simulate_DateTimeToUniversal();
			else if(op.Equals("datetime_now"))
				this.Simulate_DateTimeNow();
			else if(op.Equals("exit_program"))
				this.Simulate_Exit();
			else if(op.Equals("binaryconv_todouble"))
				this.Simulate_BinaryConvToDouble();
			else if(op.Equals("binaryconv_tofloat"))
				this.Simulate_BinaryConvToFloat();
			else if(op.Equals("binaryconv_fromdouble"))
				this.Simulate_BinaryConvFromDouble();
			else if(op.Equals("binaryconv_fromfloat"))
				this.Simulate_BinaryConvFromFloat();
			else if(op.Equals("directory_move"))
				this.Simulate_DirectoryMove();
			else if(op.Equals("directory_create"))
				this.Simulate_DirectoryCreate();
			else if(op.Equals("directory_delete"))
				this.Simulate_DirectoryDelete();
			else if(op.Equals("directory_getfilesordirs"))
				this.Simulate_DirectoryGetFilesOrDirs();
			else if(op.Equals("directory_exists"))
				this.Simulate_DirectoryExists();
			else if(op.Equals("directory_current"))
				this.Simulate_DirectoryCurrent();
			else if(op.Equals("file_copy"))
				this.Simulate_FileCopy();
			else if(op.Equals("file_delete"))
				this.Simulate_FileDelete();
			else if(op.Equals("file_exists"))
				this.Simulate_FileExists();
			else if(op.Equals("file_move"))
				this.Simulate_FileMove();
			else if(op.Equals("filestream_init"))
				this.Simulate_FileStreamInit();
			else if(op.Equals("filestream_close"))
				this.Simulate_FileStreamClose();
			else if(op.Equals("filestream_read"))
				this.Simulate_FileStreamRead();
			else if(op.Equals("filestream_seek"))
				this.Simulate_FileStreamSeek();
			else if(op.Equals("filestream_write"))
				this.Simulate_FileStreamWrite();
			else if(op.Equals("filestream_length"))
				this.Simulate_FileStreamLength();
			else if(op.Equals("fsaccess_getinfo"))
				this.Simulate_FSAccessGetInfo();
			else if(op.Equals("fsaccess_setinfo"))
				this.Simulate_FSAccessSetInfo();
			else if(op.Equals("path_normalize"))
				this.Simulate_PathNormalize();
			else if(op.Equals("stdin_init"))
				this.Simulate_StdInInit();
			else if(op.Equals("stdin_readchar"))
				this.Simulate_StdInRead();
			else if(op.Equals("stdout_init"))
				this.Simulate_StdOutInit();
			else if(op.Equals("stdout_writechar"))
				this.Simulate_StdOutWrite();
			else if(op.Equals("pow_double"))
				this.Simulate_PowDouble();
			else if(op.Equals("sqrt_double"))
				this.Simulate_SqrtDouble();
			else if(op.Equals("sin_double"))
				this.Simulate_SinDouble();
			else if(op.Equals("cos_double"))
				this.Simulate_CosDouble();
			else if(op.Equals("tan_double"))
				this.Simulate_TanDouble();
			else if(op.Equals("ceiling_double"))
				this.Simulate_CeilDouble();
			else if(op.Equals("floor_double"))
				this.Simulate_FloorDouble();
			else if(op.Equals("log_double"))
				this.Simulate_LogDouble();
			else if(op.Equals("parse_as_integral"))
				this.Simulate_ParseAsIntegral();
			else if(op.Equals("format_integral_as_string"))
				this.Simulate_FormatIntegralToString();
			else if(op.Equals("parse_as_usignintegral"))
				this.Simulate_ParseAsUIntegral();
			else if(op.Equals("format_usignintegral_as_string"))
				this.Simulate_FormatUIntegralToString();
			else if(op.Equals("parse_as_floatingpoint"))
				this.Simulate_ParseAsFloat();
			else if(op.Equals("format_floatingpoint_as_string"))
				this.Simulate_FormatFloatToString();
			else if(op.Equals("internal_string_formatbuild"))
				this.Simulate_FormatToString();
			else
				Debug.Assert(false, String.Format("Unknown Builtin Operation: {0}", op));
		}

		///////////////////////
		//Helpers

		private string PopStringPtrReadAsStringValue_Helper()
		{
			PtrValue pv = this.m_evalstack.PopPointerNoTypeConvert();
			CObject strobj = ResolvePointerTarget(pv);

			IRMemberField charsfield = ((IRClassType)strobj.LocationType).TypeDescription.GetFieldWithNameDefIn("sval");
			PtrValue charspv = strobj.ReadPassablePtrValueFromField(charsfield);

			CArray chararray = (CArray)ResolvePointerTarget(charspv);
			int len = (int)chararray.ReadArrayLength().ReadSignedValue();
			char[] ca = new char[len];

			IRSignedIntegerType i32type = GlobalInterpreterState.TProg.TypeTable.Int32Type;
			for(int i = 0; i < len; ++i)
			{
				NumericValue posv = NumericValue.MakeSignedValueOfType(i, i32type);
				NumericValue cval = chararray.ReadPassableNumericFromIndex(posv);
				ca[i] = (char)cval.ReadUnSignedValue();
			}

			return new String(ca);
		}

		private void PushStringPtrFromCSString_Helper(string toconvert)
		{
			if(toconvert.Length == 0)
			{
				IRClassType strtype = (IRClassType)GlobalInterpreterState.TProg.LookupTypeForName("System.String");
				IRStaticField emptysf = GlobalInterpreterState.TProg.NameTable.LookupStaticField(strtype, "Empty");

				this.SimulateLoadFromGlobal(emptysf, GlobalInterpreterState.GlobalFieldsMap);
			}
			else
			{
				IRSignedIntegerType i32type = GlobalInterpreterState.TProg.TypeTable.Int32Type;
				IRUnSignedIntegerType chartype = GlobalInterpreterState.TProg.TypeTable.CharType;

				IRArrayType chararray = (IRArrayType)GlobalInterpreterState.TProg.LookupTypeForName("System.Char[]");
				CArray arr = MemoryState.AllocateArrayObject(chararray, NumericValue.MakeSignedValueOfType(toconvert.Length, i32type));

				int hashcode = 0;
				for(int i = 0; i < toconvert.Length; ++i)
				{
					NumericValue cval = NumericValue.MakeUnSignedValueOfType(toconvert[i], chartype);
					NumericValue idx = NumericValue.MakeSignedValueOfType(i, i32type);

					arr.StorePassableNumericValToIndex(cval, idx);

					hashcode ^= toconvert[i];
					hashcode = hashcode << 4;
				}

				IRClassType strtype = (IRClassType)GlobalInterpreterState.TProg.LookupTypeForName("System.String");
				IRMemberField scontentsf = strtype.TypeDescription.LookupFieldInClass("sval");
				IRMemberField shashssf = strtype.TypeDescription.LookupFieldInClass("hashcode");
				IRPtrType sptr = GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor(strtype);

				CObject sloc = MemoryState.AllocateStdHeapObject(strtype);

				PtrValue apv = new PtrValue(arr.Address, GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor(chararray));
				sloc.StorePassablePtrValToField(apv, scontentsf);

				NumericValue hnv = NumericValue.MakeSignedValueOfType(hashcode, i32type);
				sloc.StorePassableNumericValToField(hnv, shashssf);

				PtrValue spv = new PtrValue(sloc.Address, sptr);
				this.m_evalstack.PushPointerEvalEntry(spv);
			}
		}

		///////////////////////
		//Arrays

		private void Simulate_ArrayClone()
		{
			PtrValue pv = this.m_evalstack.PopPointerNoTypeConvert();
			CArray aobj = (CArray)ResolvePointerTarget(pv);

			NumericValue len = aobj.ReadArrayLength();
			CArray toobj = MemoryState.AllocateArrayObject((IRArrayType)aobj.LocationType, len);
			toobj.InterpCopyArrayIntoThis_Clobber(aobj, 0, 0, (int)len.ReadSignedValue());

			IRPtrType aptype = GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor((IRArrayType)toobj.LocationType);
			PtrValue cpv = new PtrValue(toobj.Address, aptype);
			this.m_evalstack.PushPointerEvalEntry(cpv);
		}

     	private void Simulate_ArrayResize()
		{
			NumericValue newsize = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Int32Type);

			this.m_evalstack.Dup(); //dup the ref
			this.SimulateDeRefIndirectRead(); //read the ptr value

			PtrValue pv = this.m_evalstack.PopPointerNoTypeConvert();
			CArray oarray = (CArray)ResolvePointerTarget(pv);
			NumericValue currentsize = oarray.ReadArrayLength();
			if(newsize.ReadSignedValue() <= currentsize.ReadSignedValue())
				this.m_evalstack.DiscardPop(); //just drop the duplicated reference
			else
			{
				CArray newarray = MemoryState.AllocateArrayObject((IRArrayType)oarray.LocationType, newsize);
				newarray.InterpCopyArrayIntoThis_Clobber(oarray, 0, 0, (int)currentsize.ReadSignedValue());

				PtrValue npv = new PtrValue(newarray.Address, GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor((IRArrayType)newarray.LocationType));
				this.m_evalstack.PushPointerEvalEntry(npv);

				this.SimulateDeRefIndirectWrite(); //write the new ptr via the ref
			}
		}

		///////////////////////
		//Comparision

		private void Simulate_ValuetypeEqualsStatic()
		{
			this.m_evalstack.AssertTopTwoEntriesAreSameType();

			CompositeValueEntry[] cve1 = this.m_evalstack.PopStruct();
			CompositeValueEntry[] cve2 = this.m_evalstack.PopStruct();

			bool ok = true;
			for(int i = 0; i < cve1.Length; ++i)
			{
				IRType ltype = cve1[i].Offset.PathLast.FieldType;

				if(ltype is IRPtrType)
					ok &= cve1[i].Value.ConvertToPassablePtrValue().TargetAddress == cve2[i].Value.ConvertToPassablePtrValue().TargetAddress;
				else
				{
					IRPrimitiveNumericType pnt = (ltype is IRPrimitiveIntegerType) ? (IRPrimitiveNumericType)ltype : ((IREnumType)ltype).UnerlyingNumericType;
					
					if(pnt is IRSignedIntegerType)
						ok &= cve1[i].Value.ConvertToPassableNumericValue().ReadSignedValue() == cve2[i].Value.ConvertToPassableNumericValue().ReadSignedValue();
					else if(pnt is IRUnSignedIntegerType)
						ok &= cve1[i].Value.ConvertToPassableNumericValue().ReadUnSignedValue() == cve2[i].Value.ConvertToPassableNumericValue().ReadUnSignedValue();
					else
						ok &= cve1[i].Value.ConvertToPassableNumericValue().ReadFloatValue() == cve2[i].Value.ConvertToPassableNumericValue().ReadFloatValue();
				}
			}

			NumericValue nv = NumericValue.MakeSignedValueOfType(ok ? 1 : 0, GlobalInterpreterState.TProg.TypeTable.Int32Type);
			this.m_evalstack.PushNumericEvalEntry(nv);
		}

		private void Simulate_ValuetypeHashcodeStatic()
		{
			this.SimulateDeRefIndirectRead();

			CompositeValueEntry[] cve = this.m_evalstack.PopStruct();
			int hv = 0;

			for(int i = 0; i < cve.Length; ++i)
			{
				IRType ltype = cve[i].Offset.PathLast.FieldType;

				if(ltype is IRPtrType)
					hv ^= cve[i].Value.ConvertToPassablePtrValue().TargetAddress;
				else
				{
					IRPrimitiveNumericType pnt = (ltype is IRPrimitiveIntegerType) ? (IRPrimitiveNumericType)ltype : ((IREnumType)ltype).UnerlyingNumericType;

					if(pnt is IRSignedIntegerType)
						hv ^= cve[i].Value.ConvertToPassableNumericValue().ReadSignedValue().GetHashCode();
					else if(pnt is IRUnSignedIntegerType)
						hv ^= cve[i].Value.ConvertToPassableNumericValue().ReadUnSignedValue().GetHashCode();
					else
						hv ^= cve[i].Value.ConvertToPassableNumericValue().ReadFloatValue().GetHashCode();
				}
			}

			NumericValue nv = NumericValue.MakeSignedValueOfType(hv, GlobalInterpreterState.TProg.TypeTable.Int32Type);
			this.m_evalstack.PushNumericEvalEntry(nv);
		}

		///////////////////////
		//DateTime

		private void Simulate_DateTimeToUniversal()
		{
			throw new NotImplementedException("Need to add builtin support");
		}

		private void Simulate_DateTimeToLocal()
		{
			throw new NotImplementedException("Need to add builtin support");
		}

		private void Simulate_DateTimeNow()
		{
			throw new NotImplementedException("Need to add builtin support");
		}

		///////////////////////
		//Envoronment and state

		private void Simulate_Exit()
		{
			int code = (int)this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Int32Type).ReadSignedValue();
			GlobalInterpreterState.ExitProgram(code);
		}

		///////////////////////
		//Binary Reader/Writer

		private void Simulate_BinaryConvToDouble()
		{
			NumericValue nv = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Float64Type);
			this.m_evalstack.PushNumericEvalEntry(nv);
		}

		private void Simulate_BinaryConvToFloat()
		{
			NumericValue nv = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Float32Type);
			this.m_evalstack.PushNumericEvalEntry(nv);
		}

		private void Simulate_BinaryConvFromDouble()
		{
			NumericValue nv = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.UInt64Type);
			this.m_evalstack.PushNumericEvalEntry(nv);
		}

		private void Simulate_BinaryConvFromFloat()
		{
			NumericValue nv = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.UInt32Type);
			this.m_evalstack.PushNumericEvalEntry(nv);
		}

		///////////////////////
		//Directory operations

		private void Simulate_DirectoryMove()
		{
			string dest = this.PopStringPtrReadAsStringValue_Helper();
			string source = this.PopStringPtrReadAsStringValue_Helper();

			System.IO.Directory.Move(source, dest);
		}

		private void Simulate_DirectoryCreate()
		{
			string trgt = this.PopStringPtrReadAsStringValue_Helper();
			
			System.IO.Directory.CreateDirectory(trgt);
		}

		private void Simulate_DirectoryDelete()
		{
			string trgt = this.PopStringPtrReadAsStringValue_Helper();

			System.IO.Directory.Delete(trgt);
		}

		private void Simulate_DirectoryGetFilesOrDirs()
		{
			IRSignedIntegerType i32type = GlobalInterpreterState.TProg.TypeTable.Int32Type;

			int filesordirs = (int)this.m_evalstack.PopNumericValueAsType(i32type).ReadSignedValue();
			int sopt = (int)this.m_evalstack.PopNumericValueAsType(i32type).ReadSignedValue();
			string match = this.PopStringPtrReadAsStringValue_Helper();
			string s = this.PopStringPtrReadAsStringValue_Helper();

			string[] enums;
			if(filesordirs == 1)
				enums = System.IO.Directory.EnumerateFiles(s, match, (System.IO.SearchOption)sopt).ToArray();
			else
				enums = System.IO.Directory.EnumerateDirectories(s, match, (System.IO.SearchOption)sopt).ToArray();

			IRArrayType enarraytype = (IRArrayType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForName("System.String[]");
			CArray enarray = MemoryState.AllocateArrayObject(enarraytype, NumericValue.MakeSignedValueOfType(enums.Length, i32type));

			IRClassType stringtype = (IRClassType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForName("System.String");
			IRPtrType stringptrtype = GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor(stringtype);
			for(int i = 0; i < enums.Length; ++i)
			{
				this.m_evalstack.PushPointerEvalEntry(new PtrValue(enarray.Address, stringptrtype));
				this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(i, i32type));
				this.PushStringPtrFromCSString_Helper(enums[i]);

				this.SimulateArrayStore(stringptrtype);
			}

			PtrValue rpv = new PtrValue(enarray.Address, GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor(enarraytype));
			this.m_evalstack.PushPointerEvalEntry(rpv);
		}

		private void Simulate_DirectoryExists()
		{
			string trgt = this.PopStringPtrReadAsStringValue_Helper();

			int exists = System.IO.Directory.Exists(trgt) ? 1 : 0;
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(exists, GlobalInterpreterState.TProg.TypeTable.BoolType));
		}

		private void Simulate_DirectoryCurrent()
		{
			string currdir = System.IO.Directory.GetCurrentDirectory();

			this.PushStringPtrFromCSString_Helper(currdir);
		}

		///////////////////////
		//File operations

		private void Simulate_FileCopy()
		{
			string dest = this.PopStringPtrReadAsStringValue_Helper();
			string source = this.PopStringPtrReadAsStringValue_Helper();

			System.IO.File.Copy(source, dest);
		}

		private void Simulate_FileDelete()
		{
			string trgt = this.PopStringPtrReadAsStringValue_Helper();

			System.IO.File.Delete(trgt);
		}

		private void Simulate_FileExists()
		{
			string trgt = this.PopStringPtrReadAsStringValue_Helper();

			int exists = System.IO.File.Exists(trgt) ? 1 : 0;
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(exists, GlobalInterpreterState.TProg.TypeTable.BoolType));
		}

		private void Simulate_FileMove()
		{
			string dest = this.PopStringPtrReadAsStringValue_Helper();
			string source = this.PopStringPtrReadAsStringValue_Helper();

			System.IO.File.Move(source, dest);
		}

		///////////////////////
		//FileStream operations

		private System.IO.FileStream PopPtrReadAllDescriptorsReturnUnderlyFS()
		{
			PtrValue pv = this.m_evalstack.PopPointerNoTypeConvert();
			CObject fsobj = ResolvePointerTarget(pv);

			IRClassType fstype = (IRClassType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForName("System.IO.FileStream");
			IRMemberField fmodefield = fstype.TypeDescription.GetFieldWithNameDefIn("fmode");
			IRMemberField faccessfield = fstype.TypeDescription.GetFieldWithNameDefIn("faccess");
			IRMemberField fsharefield = fstype.TypeDescription.GetFieldWithNameDefIn("fshare");
			IRMemberField fstatefield = fstype.TypeDescription.GetFieldWithNameDefIn("state");

			fsobj.ReadPassableNumericFromField(fmodefield);
			fsobj.ReadPassableNumericFromField(faccessfield);
			fsobj.ReadPassableNumericFromField(fsharefield);

			int fdescriptor = (int)fsobj.ReadPassableNumericFromField(fstatefield).ReadSignedValue();
			System.IO.FileStream fs = FileStateAndSystem.FileSystemMap[fdescriptor];

			return fs;
		}

		private void Simulate_FileStreamInit()
		{
			string path = this.PopStringPtrReadAsStringValue_Helper();

			PtrValue pv = this.m_evalstack.PopPointerNoTypeConvert();
			CObject fsobj = ResolvePointerTarget(pv);

			IRClassType fstype = (IRClassType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForName("System.IO.FileStream");
			IRMemberField fmodefield = fstype.TypeDescription.GetFieldWithNameDefIn("fmode");
			IRMemberField faccessfield = fstype.TypeDescription.GetFieldWithNameDefIn("faccess");
			IRMemberField fsharefield = fstype.TypeDescription.GetFieldWithNameDefIn("fshare");
			IRMemberField fstatefield = fstype.TypeDescription.GetFieldWithNameDefIn("state");

			System.IO.FileMode fmode = (System.IO.FileMode)fsobj.ReadPassableNumericFromField(fmodefield).ReadSignedValue();
			System.IO.FileAccess faccess = (System.IO.FileAccess)fsobj.ReadPassableNumericFromField(faccessfield).ReadSignedValue();
			System.IO.FileShare fshare = (System.IO.FileShare)fsobj.ReadPassableNumericFromField(fsharefield).ReadSignedValue();

			IRSignedIntegerType i32type = GlobalInterpreterState.TProg.TypeTable.Int32Type;
			int ffield = FileStateAndSystem.OpenFileAddDesciptor(path, fmode, faccess, fshare);
			fsobj.StorePassableNumericValToField(NumericValue.MakeSignedValueOfType(ffield, i32type), fstatefield);
		}

		private void Simulate_FileStreamClose()
		{
			System.IO.FileStream fs = this.PopPtrReadAllDescriptorsReturnUnderlyFS();
			fs.Close();
		}

		private void Simulate_FileStreamRead()
		{
			IRUnSignedIntegerType bytetype = GlobalInterpreterState.TProg.TypeTable.UInt8Type;
			IRSignedIntegerType i32type = GlobalInterpreterState.TProg.TypeTable.Int32Type;

			int count = (int)this.m_evalstack.PopNumericValueAsType(i32type).ReadSignedValue();
			int offset = (int)this.m_evalstack.PopNumericValueAsType(i32type).ReadSignedValue();

			PtrValue bytearrayptr = this.m_evalstack.PopPointerNoTypeConvert();
			CArray bytearray = (CArray)ResolvePointerTarget(bytearrayptr);

			System.IO.FileStream fs = this.PopPtrReadAllDescriptorsReturnUnderlyFS();
			byte[] barray = new byte[count];
			int actual = fs.Read(barray, 0, count);

			for(int i = 0; i < actual; ++i)
				bytearray.StorePassableNumericValToIndex(NumericValue.MakeUnSignedValueOfType(barray[i], bytetype), NumericValue.MakeSignedValueOfType(offset + i, i32type));

			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(actual, i32type));
		}

		private void Simulate_FileStreamSeek()
		{
			System.IO.SeekOrigin sorig = (System.IO.SeekOrigin)this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Int32Type).ReadSignedValue();
			long offset = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Int64Type).ReadSignedValue();
			System.IO.FileStream fs = this.PopPtrReadAllDescriptorsReturnUnderlyFS();

			fs.Seek(offset, sorig);
		}

		private void Simulate_FileStreamWrite()
		{
			IRSignedIntegerType i32type = GlobalInterpreterState.TProg.TypeTable.Int32Type;

			int count = (int)this.m_evalstack.PopNumericValueAsType(i32type).ReadSignedValue();
			int offset = (int)this.m_evalstack.PopNumericValueAsType(i32type).ReadSignedValue();

			PtrValue bytearrayptr = this.m_evalstack.PopPointerNoTypeConvert();
			CArray bytearray = (CArray)ResolvePointerTarget(bytearrayptr);

			System.IO.FileStream fs = this.PopPtrReadAllDescriptorsReturnUnderlyFS();
			byte[] barray = new byte[count];
			
			for(int i = 0; i < count; ++i)
				barray[i] = (byte)bytearray.ReadPassableNumericFromIndex(NumericValue.MakeSignedValueOfType(offset + i, i32type)).ReadUnSignedValue();

			fs.Write(barray, offset, count);
		}

		private void Simulate_FileStreamLength()
		{
			System.IO.FileStream fs = this.PopPtrReadAllDescriptorsReturnUnderlyFS();

			long flen = fs.Length;
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(flen, GlobalInterpreterState.TProg.TypeTable.Int64Type));
		}

		///////////////////////
		//FSAccessInfo

		private void Simulate_FSAccessGetInfo()
		{
			throw new NotImplementedException("Need to add builtin support");
		}

		private void Simulate_FSAccessSetInfo()
		{
			throw new NotImplementedException("Need to add builtin support");
		}

		///////////////////////
		//Path

		private void Simulate_PathNormalize()
		{
			bool fullcheck = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.BoolType).ReadSignedValue() == 0;
			string pn = this.PopStringPtrReadAsStringValue_Helper();

			string normpn = System.IO.Path.GetFullPath(pn);
			if(!fullcheck && normpn.EndsWith("\\"))
				normpn = normpn.Substring(0, normpn.Length - 2);

			this.PushStringPtrFromCSString_Helper(normpn);
		}

		///////////////////////
		//Stdin/Stdout

		private void Simulate_StdInInit()
		{
			return;
		}

		private void Simulate_StdInRead()
		{
			PtrValue pv = this.m_evalstack.PopPointerNoTypeConvert();
			CObject ov = ResolvePointerTarget(pv);

			IRMemberField sfield = ((IRClassType)ov.LocationType).TypeDescription.GetFieldWithNameDefIn("state");
			ov.ReadPassableNumericFromField(sfield);

			int rval = System.Console.Read();
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(rval, GlobalInterpreterState.TProg.TypeTable.Int32Type));
		}

		private void Simulate_StdOutInit()
		{
			return;
		}

		private void Simulate_StdOutWrite()
		{
			char nv = (char)this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.CharType).ReadUnSignedValue();

			PtrValue pv = this.m_evalstack.PopPointerNoTypeConvert();
			CObject ov = ResolvePointerTarget(pv);

			IRMemberField sfield = ((IRClassType)ov.LocationType).TypeDescription.GetFieldWithNameDefIn("state");
			ov.ReadPassableNumericFromField(sfield);

			System.Console.Write(nv);
		}

		///////////////////////
		//Numerics

		private void Simulate_PowDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double y = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Pow(x, y);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		private void Simulate_SqrtDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Sqrt(x);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		private void Simulate_SinDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Sin(x);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		private void Simulate_CosDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Cos(x);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		private void Simulate_TanDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Tan(x);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		private void Simulate_CeilDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Ceiling(x);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		private void Simulate_FloorDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Floor(x);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		private void Simulate_LogDouble()
		{
			IRFloatingPointType dtype = GlobalInterpreterState.TProg.TypeTable.Float64Type;
			double x = this.m_evalstack.PopNumericValueAsType(dtype).ReadFloatValue();

			double rv = Math.Log(x);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(rv, dtype));
		}

		///////////////////////
		//Enum compare

		private void Simulate_EnumCompare()
		{
			this.m_evalstack.Dup();
			PtrValue tpv = this.m_evalstack.PopPointerNoTypeConvert();

			if(tpv.IsNullPtr)
			{
				this.m_evalstack.DiscardPop();
				this.m_evalstack.DiscardPop();
				this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(0, GlobalInterpreterState.TProg.TypeTable.BoolType));
			}
			else
			{
				IREnumType enumtype = (IREnumType)((IRReferenceType)this.m_evalstack.PeekTypeK(1)).ReferenceTargetType;

				this.SimulateUnBox(enumtype, 1);
				this.SimulateDeRefIndirectRead();

				this.SimulatePrimEQ();
			}
		}

		///////////////////////
		//Pasring numerics

		private void Simulate_ParseAsIntegral()
		{
			string sv = this.PopStringPtrReadAsStringValue_Helper();
			long pval = Int64.Parse(sv);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(pval, GlobalInterpreterState.TProg.TypeTable.Int64Type));
		}

		private void Simulate_FormatIntegralToString()
		{
			long pv = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Int64Type).ReadSignedValue();
			string sv = pv.ToString();
			this.PushStringPtrFromCSString_Helper(sv);
		}

		private void Simulate_ParseAsUIntegral()
		{
			string sv = this.PopStringPtrReadAsStringValue_Helper();
			ulong pval = UInt64.Parse(sv);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeUnSignedValueOfType(pval, GlobalInterpreterState.TProg.TypeTable.UInt64Type));
		}

		private void Simulate_FormatUIntegralToString()
		{
			ulong pv = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.UInt64Type).ReadUnSignedValue();
			string sv = pv.ToString();
			this.PushStringPtrFromCSString_Helper(sv);
		}

		private void Simulate_ParseAsFloat()
		{
			string sv = this.PopStringPtrReadAsStringValue_Helper();
			double pval = Double.Parse(sv);
			this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeFloatValueOfType(pval, GlobalInterpreterState.TProg.TypeTable.Float64Type));
		}

		private void Simulate_FormatFloatToString()
		{
			double pv = this.m_evalstack.PopNumericValueAsType(GlobalInterpreterState.TProg.TypeTable.Float64Type).ReadFloatValue();
			string sv = pv.ToString();
			this.PushStringPtrFromCSString_Helper(sv);
		}

		///////////////////////
		//String formatting

		private void Simulate_FormatToString()
		{
			IRClassType stringtype = (IRClassType)GlobalInterpreterState.TProg.TypeTable.LookupTypeForName("System.String");
			IRPtrType stringptrtype = GlobalInterpreterState.TProg.TypeTable.LookupPointerTypeFor(stringtype);

			PtrValue argsapv = this.m_evalstack.PopPointerNoTypeConvert();
			CArray argsa = (CArray)ResolvePointerTarget(argsapv);
			int len = (int)argsa.ReadArrayLength().ReadSignedValue();

			string[] args = new string[len];
			for(int i = 0; i < len; ++i)
			{
				PtrValue spv = argsa.ReadPassablePtrValueFromIndex(NumericValue.MakeSignedValueOfType(i, GlobalInterpreterState.TProg.TypeTable.Int32Type));
				this.m_evalstack.PushPointerEvalEntry(spv);

				args[i] = this.PopStringPtrReadAsStringValue_Helper();
			}

			string format = PopStringPtrReadAsStringValue_Helper();

			string fms = String.Format(format, args);
			this.PushStringPtrFromCSString_Helper(fms);
		}

		///////////////////////
		//Value type operations

		private void Simulate_ValuetypeEquals()
		{
			this.m_evalstack.Dup();
			PtrValue tpv = this.m_evalstack.PopPointerNoTypeConvert();

			if(tpv.IsNullPtr)
			{
				this.m_evalstack.DiscardPop();
				this.m_evalstack.DiscardPop();
				this.m_evalstack.PushNumericEvalEntry(NumericValue.MakeSignedValueOfType(0, GlobalInterpreterState.TProg.TypeTable.BoolType));
			}
			else
			{
				IRValueType valtype = (IRValueType)((IRReferenceType)this.m_evalstack.PeekTypeK(1)).ReferenceTargetType;

				this.SimulateUnBox(valtype, 1);
				this.SimulateDeRefIndirectRead();

				this.Simulate_ValuetypeEqualsStatic();
			}
		}

		private void Simulate_ValuetypeHashcode()
		{
			this.SimulateDeRefIndirectRead();
			this.Simulate_ValuetypeHashcodeStatic();
		}
	}
}
